package com.learn.security.utils.signature;

import com.learn.security.utils.BaseUtil;

import java.security.*;
import java.security.interfaces.DSAPrivateKey;
import java.security.interfaces.DSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;

/**
 * DSA签名--也是非对称加密
 * <p>
 * 只能进行签名，不能进行加密通信
 */
public class DSAUtil extends BaseUtil {

    public static final String ALGORITHM = "DSA";

    /**
     * 默认密钥字节数
     *
     * <pre>
     * DSA
     * Default Keysize 1024
     * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
     * </pre>
     */
    public static final int KEY_SIZE = 1024;

    /**
     * 默认种子
     */
    public static final String DEFAULT_SEED = "0f22507a10bbddd07d8a3082122966e3";

    public static final String PUBLIC_KEY = "DSAPublicKey";
    public static final String PRIVATE_KEY = "DSAPrivateKey";

    /**
     * 生成密钥
     *
     * @param seed    种子
     * @param keySize 密钥字节数
     * @return 装秘钥和公钥的map
     */
    public static Map<String, Object> initKey(String seed, int keySize) {

        Map<String, Object> map = new HashMap<>();

        try {
            KeyPairGenerator keygen = KeyPairGenerator.getInstance(ALGORITHM);
            // 初始化随机产生器
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.setSeed(seed.getBytes());
            keygen.initialize(keySize, secureRandom);

            KeyPair keys = keygen.genKeyPair();

            DSAPublicKey publicKey = (DSAPublicKey) keys.getPublic();
            DSAPrivateKey privateKey = (DSAPrivateKey) keys.getPrivate();

            map.put(PUBLIC_KEY, publicKey);
            map.put(PRIVATE_KEY, privateKey);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return map;
    }

    /**
     * 默认生成密钥
     *
     * @param size 密钥字节数
     * @return 装秘钥和公钥的map
     */
    public static Map<String, Object> initKey(int size) {
        return initKey(DEFAULT_SEED, size);
    }

    /**
     * 默认生成密钥
     *
     * @return 装秘钥和公钥的map
     */
    public static Map<String, Object> initKey() {
        return initKey(DEFAULT_SEED, KEY_SIZE);
    }

    /**
     * 取得私钥
     *
     * @param keyMap 装秘钥和公钥的map
     * @return 私钥--base64加密的字符
     */
    public static String getPrivateKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PRIVATE_KEY);

        return encryptBASE64(key.getEncoded());
    }

    /**
     * 取得公钥
     *
     * @param keyMap 装秘钥和公钥的map
     * @return 公钥--base64加密的字符
     */
    public static String getPublicKey(Map<String, Object> keyMap) {
        Key key = (Key) keyMap.get(PUBLIC_KEY);

        return encryptBASE64(key.getEncoded());
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       加密数据
     * @param privateKey 私钥--base64加密的字符
     * @return 数字签名--base64加密的字符
     */
    public static String sign(byte[] data, String privateKey) {
        // 解密由base64编码的私钥
        byte[] keyBytes = decryptBASE64(privateKey);

        try {
            if (null != keyBytes) {
                // 构造PKCS8EncodedKeySpec对象
                PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

                // KEY_ALGORITHM 指定的加密算法
                KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

                // 取私钥匙对象
                PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

                // 用私钥对信息生成数字签名
                Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
                signature.initSign(priKey);
                signature.update(data);

                return encryptBASE64(signature.sign());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 校验数字签名
     *
     * @param data      加密数据
     * @param publicKey 公钥--base64加密的字符
     * @param sign      数字签名--base64加密的字符
     * @return 校验成功返回true 失败返回false
     */
    public static Boolean verify(byte[] data, String publicKey, String sign) {

        // 解密由base64编码的公钥
        byte[] keyBytes = decryptBASE64(publicKey);

        try {
            if (null != keyBytes) {
                // 构造X509EncodedKeySpec对象
                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

                // ALGORITHM 指定的加密算法
                KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);

                // 取公钥匙对象
                PublicKey pubKey = keyFactory.generatePublic(keySpec);

                Signature signature = Signature.getInstance(keyFactory.getAlgorithm());
                signature.initVerify(pubKey);
                signature.update(data);

                // 验证签名是否正常
                return signature.verify(decryptBASE64(sign));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}
